﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows.Forms;
using DevExpress.Utils;
using DevExpress.Utils.Menu;
using DevExpress.XtraBars;
using DevExpress.XtraEditors.Controls;
using DevExpress.XtraEditors.DXErrorProvider;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraGrid.Menu;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraGrid.Views.Grid.ViewInfo;
using DevExpress.XtraPrinting;
using DevExpress.XtraVerticalGrid;
using DevExpress.XtraVerticalGrid.Events;
using DevExpress.XtraVerticalGrid.Rows;
using Loki.Utils;

namespace Loki.UI.Win
{
    public static class GridExtensions
    {
        static GridExtensions()
        {
            _Log = LokiServices.Logger.GetLogger("Loki.UI.Win.GridExtensions");
        }
        static ILog _Log;

        public static bool IsValidRowHandle(this GridView P_Grid, int P_RowHandle)
        {
            return P_RowHandle != GridControl.AutoFilterRowHandle
                && P_RowHandle != GridControl.InvalidRowHandle
                && P_RowHandle != GridControl.NewItemRowHandle;
        }

        public static void Bind(this GridColumn P_Column, DataTable P_Datasource, string P_ColumnName, RepositoryItem P_Editor = null)
        {
            P_Column.FieldName = P_ColumnName;
            if (P_Editor != null)
            {
                P_Column.ColumnEdit = P_Editor;
                P_Column.DisplayFormat.Assign(P_Editor.DisplayFormat);
            }
            if (P_Datasource.Columns.IndexOf(P_ColumnName) > 0
                && P_Datasource.Columns[P_ColumnName].DataType == typeof(DateTime)
                )
            {
                P_Column.OptionsFilter.FilterPopupMode = FilterPopupMode.List;
            }
        }

        public static void Bind<TDisplayable>(
            this EditorRow P_Column,
            Expression<Func<TDisplayable, object>> P_Property,
            RepositoryItem P_Editor = null,
            ColumnRule<TDisplayable> P_Rule = null) where TDisplayable : class
        {
            PropertyInfo L_Property = ExpressionHelper.Reflect<TDisplayable>.GetProperty(P_Property);
            P_Column.Properties.FieldName = L_Property.Name;

            if (P_Editor != null)
            {
                P_Column.Properties.RowEdit = P_Editor;
                P_Column.Properties.Format.Assign(P_Editor.DisplayFormat);
            }

            if (P_Rule != null)
            {
                P_Column.Grid.ValidateRecord += delegate(object sender, ValidateRecordEventArgs e)
                {
                    TDisplayable L_Item = P_Column.Grid.GetRecordObject(e.RecordIndex) as TDisplayable;
                    if (!P_Rule.Validator(L_Item))
                    {
                        e.Valid = false;
                        e.ErrorText = P_Rule.ErrorMessageBuilder(L_Item);
                        P_Column.Grid.SetRowError(P_Column.Properties, e.ErrorText);
                        return;
                    }
                    else
                    {
                        P_Column.Grid.SetRowError(P_Column.Properties, string.Empty);
                    }
                };
            }
        }


        public static void Bind<TDisplayable>(
            this GridColumn P_Column,
            Expression<Func<TDisplayable, object>> P_Property,
            RepositoryItem P_Editor = null,
            ColumnRule<TDisplayable> P_Rule = null) where TDisplayable : class
        {
            PropertyInfo L_Property = ExpressionHelper.Reflect<TDisplayable>.GetProperty(P_Property);
            P_Column.FieldName = L_Property.Name;
            if (L_Property.PropertyType == typeof(DateTime)
                || L_Property.PropertyType == typeof(DateTime?))
            {
                P_Column.OptionsFilter.FilterPopupMode = FilterPopupMode.List;
            }

            if (P_Editor != null)
            {
                P_Column.ColumnEdit = P_Editor;
                P_Column.DisplayFormat.Assign(P_Editor.DisplayFormat);
            }

            if (P_Rule != null)
            {
                P_Column.View.ValidateRow += delegate(object sender, ValidateRowEventArgs e)
                {
                    TDisplayable L_Item = e.Row as TDisplayable;
                    if (!P_Rule.Validator(L_Item))
                    {
                        e.Valid = false;
                        e.ErrorText = P_Rule.ErrorMessageBuilder(L_Item);
                        P_Column.View.SetColumnError(P_Column, e.ErrorText);
                        return;
                    }
                    else
                    {
                        P_Column.View.SetColumnError(P_Column, string.Empty);
                    }
                };
            }
        }

        public static void Configure(this GridView P_Grid, DataTable P_DataSource)
        {
            // apparence
            ConfigureApparence(P_Grid);

            // menu
            ConfigureGridCommands(P_Grid, P_DataSource);

            if (P_Grid.GridControl != null)
            {
                P_Grid.GridControl.DataSource = P_DataSource;
            }
        }

        public static void Configure<TDisplayable>(this GridView P_Grid, LokiCollection<TDisplayable> P_DataSource, Func<TDisplayable, string> P_ConfirmMessage = null, GridRule<TDisplayable>[] P_Rules = null, Action<TDisplayable> P_PostValidationAction = null) where TDisplayable : class
        {
            // apparence
            ConfigureApparence(P_Grid);

            // validation
            ConfigureGridValidation<TDisplayable>(P_Grid, P_Rules, P_PostValidationAction);

            // dirty rule
            ConfigureGridDirtyRule(P_Grid);

            // key shortcuts
            ConfigureGridKeyboardShortCuts(P_Grid, P_DataSource, P_ConfirmMessage);

            // menu
            ConfigureGridCommands(P_Grid, P_DataSource);

            if (P_Grid.GridControl != null)
            {
                P_Grid.GridControl.DataSource = P_DataSource;
            }
        }

        public static void Configure<TDisplayable>(this VGridControl P_Grid, LokiCollection<TDisplayable> P_DataSource, Func<TDisplayable, string> P_ConfirmMessage = null, GridRule<TDisplayable>[] P_Rules = null, Action<TDisplayable> P_PostValidationAction = null) where TDisplayable : class
        {
            // key shortcuts
            ConfigureGridKeyboardShortCuts(P_Grid, P_DataSource, P_ConfirmMessage);

            // menu
            ConfigureGridCommands(P_Grid, P_DataSource);

            if (P_Grid != null)
            {
                P_Grid.DataSource = P_DataSource;
            }
        }

        public static void ConfigureApparence(this GridView P_Grid)
        {
            P_Grid.OptionsSelection.MultiSelect = true;
            P_Grid.OptionsSelection.UseIndicatorForSelection = true;
            P_Grid.OptionsSelection.MultiSelectMode = GridMultiSelectMode.RowSelect;
            P_Grid.OptionsView.EnableAppearanceEvenRow = true; ;
            P_Grid.OptionsView.EnableAppearanceOddRow = true; ;
            if (P_Grid.OptionsBehavior.Editable)
            {
                P_Grid.OptionsView.NewItemRowPosition = NewItemRowPosition.Top;
                foreach (GridColumn L_Column in P_Grid.Columns)
                {
                    if (L_Column.OptionsColumn.ReadOnly)
                        L_Column.AppearanceCell.BackColor = Color.LightSteelBlue;
                }
            }
            else
                P_Grid.OptionsView.NewItemRowPosition = NewItemRowPosition.None;
        }

        public static void ConfigureGridDirtyRule(this GridView P_View)
        {
            if (P_View.OptionsBehavior.Editable)
            {
                GridColumn COL_IsDirty = new GridColumn();

                // 
                // COL_IsDirty
                // 
                COL_IsDirty.Caption = "IsDirty";
                COL_IsDirty.FieldName = "IsDirty";
                COL_IsDirty.Name = P_View.Name + "_COL_IsDirty";
                COL_IsDirty.Width = 44;
                COL_IsDirty.Visible = false;
                COL_IsDirty.OptionsColumn.ShowInCustomizationForm = false;
                P_View.Columns.Add(COL_IsDirty);

                StyleFormatCondition L_Condition = new StyleFormatCondition();
                L_Condition.Appearance.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold);
                L_Condition.Appearance.Options.UseFont = true;
                L_Condition.ApplyToRow = true;
                L_Condition.Column = COL_IsDirty;
                L_Condition.Condition = DevExpress.XtraGrid.FormatConditionEnum.Equal;
                L_Condition.Value1 = true;
                P_View.FormatConditions.AddRange(new DevExpress.XtraGrid.StyleFormatCondition[] {
            L_Condition});
            }
        }

        public static void ConfigureGridValidation<TDisplayable>(GridView P_Grid, GridRule<TDisplayable>[] P_Rules, Action<TDisplayable> P_PostValidationAction) where TDisplayable : class
        {
            if (P_Grid.OptionsBehavior.Editable && P_Rules != null && P_Rules.Length > 0)
            {
                P_Grid.ValidateRow += delegate(object sender, ValidateRowEventArgs e)
                {
                    TDisplayable L_Item = e.Row as TDisplayable;
                    if (L_Item != null)
                    {
                        foreach (var L_Rule in P_Rules)
                        {
                            if (!L_Rule.Validator(L_Item))
                            {
                                e.Valid = false;
                                if (L_Rule.ErrorMessageBuilder == null)
                                    e.ErrorText = L_Rule.ErrorMessage;
                                else
                                    e.ErrorText = L_Rule.ErrorMessageBuilder(L_Item);
                                if (L_Rule.Columns == null)
                                    L_Rule.Column.View.SetColumnError(L_Rule.Column, e.ErrorText);
                                else
                                    foreach (GridColumn L_Column in L_Rule.Columns)
                                    {
                                        L_Column.View.SetColumnError(L_Column, e.ErrorText);
                                    }
                                return;
                            }
                            else
                                if (L_Rule.Columns == null)
                                    L_Rule.Column.View.SetColumnError(L_Rule.Column, string.Empty);
                                else
                                    foreach (GridColumn L_Column in L_Rule.Columns)
                                    {
                                        L_Column.View.SetColumnError(L_Column, string.Empty);
                                    }
                        }
                        if (e.Valid && P_PostValidationAction != null)
                            P_PostValidationAction(L_Item);
                    }
                };
            }
        }
        public static void ConfigureGridKeyboardShortCuts<TDisplayable>(VGridControl P_Grid, LokiCollection<TDisplayable> P_DataSource, Func<TDisplayable, string> P_ConfirmMessage) where TDisplayable : class
        {
            P_Grid.KeyDown += delegate(object sender, KeyEventArgs e)
            {
                if (P_Grid.OptionsBehavior.Editable)
                {
                    if (e.KeyCode == Keys.Delete && P_Grid.OptionsBehavior.Editable)
                    {

                        TDisplayable L_Item = P_Grid.GetRecordObject(P_Grid.FocusedRecord) as TDisplayable;
                        if (L_Item != null && MessageBoxes.Confirm(P_ConfirmMessage(L_Item)))
                        {
                            P_DataSource.Remove(L_Item);
                        }
                    }
                    if (P_Grid.EditingValue != null && e.KeyData == (Keys.V | Keys.Control))
                    {
                        PasteValues(P_Grid);
                        e.Handled = true;
                    }
                }
            };
        }

        public static void ConfigureGridKeyboardShortCuts<TDisplayable>(GridView P_Grid, LokiCollection<TDisplayable> P_DataSource, Func<TDisplayable, string> P_ConfirmMessage) where TDisplayable : class
        {
            P_Grid.GridControl.KeyDown += delegate(object sender, KeyEventArgs e)
            {
                if (P_Grid.OptionsBehavior.Editable)
                {
                    if (e.KeyCode == Keys.Delete && P_Grid.OptionsBehavior.AllowDeleteRows != DefaultBoolean.False)
                    {
                        if (P_Grid.OptionsSelection.MultiSelectMode == GridMultiSelectMode.RowSelect && P_Grid.OptionsSelection.MultiSelect && P_Grid.GetSelectedRows().Length > 1)
                        {
                            int[] L_RowHandles = P_Grid.GetSelectedRows();
                            List<TDisplayable> L_Items = new List<TDisplayable>(L_RowHandles.Length);
                            foreach (int L_Handle in L_RowHandles)
                            {
                                TDisplayable L_Item = P_Grid.GetRow(L_Handle) as TDisplayable;
                                if (L_Item != null)
                                    L_Items.Add(L_Item);
                            }
                            if (MessageBoxes.Confirm("Delete selected lines ?"))
                                L_Items.ForEach(x => P_DataSource.Remove(x));
                        }
                        else
                        {
                            TDisplayable L_Item = P_Grid.GetFocusedRow() as TDisplayable;
                            if (L_Item != null && MessageBoxes.Confirm(P_ConfirmMessage(L_Item)))
                            {
                                P_DataSource.Remove(L_Item);
                            }
                        }
                    }
                    if (!P_Grid.IsEditing && e.KeyData == (Keys.V | Keys.Control))
                    {
                        PasteValues(P_Grid);
                        e.Handled = true;
                    }
                }
            };
        }

        public static void ConfigureGridCommands(GridView P_Grid, object P_DataSource)
        {
            // menu
            if (P_Grid.GridControl != null)
            {
                Dictionary<string, Action> L_Commands = new Dictionary<string, Action>();
                L_Commands["Export.Excel"] = delegate()
                {
                    P_Grid.ExportToXls();
                };
                L_Commands["Export.PDF"] = delegate()
                {
                    P_Grid.ExportToPdf();
                };

                ViewBase L_Form = P_Grid.GridControl.FindForm() as ViewBase;
                if (L_Form != null)
                {
                    P_Grid.GridControl.MenuManager = L_Form.BMA_Main;
                    P_Grid.GridControl.ToolTipController = L_Form.TOO_Controller;
                }
                AddPopupMenu(P_Grid, L_Commands);
            }
        }

        public static void ConfigureGridCommands<TDisplayable>(GridView P_Grid, LokiCollection<TDisplayable> P_DataSource) where TDisplayable : class
        {
            // menu
            if (P_Grid.GridControl != null)
            {
                Dictionary<string, Action> L_Commands = new Dictionary<string, Action>();
                L_Commands["Export.Excel"] = delegate()
                {
                    P_Grid.ExportToXls();
                };
                L_Commands["Export.PDF"] = delegate()
                {
                    P_Grid.ExportToPdf();
                };
                if (P_Grid.OptionsBehavior.Editable)
                {
                    L_Commands["Paste"] = delegate()
                    {
                        P_Grid.PasteValues();
                    };
                    if (P_Grid.OptionsBehavior.AllowDeleteRows != DefaultBoolean.False)
                    {
                        L_Commands["Clear"] = delegate()
                        {
                            if (MessageBoxes.Confirm("Delete all lines ?"))
                            {
                                P_DataSource.Clear();
                            }
                        };
                    }
                }
                ViewBase L_Form = P_Grid.GridControl.FindForm() as ViewBase;
                if (L_Form != null)
                {
                    P_Grid.GridControl.MenuManager = L_Form.BMA_Main;
                    P_Grid.GridControl.ToolTipController = L_Form.TOO_Controller;
                }
                AddPopupMenu(P_Grid, L_Commands);
            }
        }

        public static void ConfigureGridCommands<TDisplayable>(VGridControl P_Grid, LokiCollection<TDisplayable> P_DataSource) where TDisplayable : class
        {
            // menu
            if (P_Grid != null)
            {
                Dictionary<string, Action> L_Commands = new Dictionary<string, Action>();
                L_Commands["Export.Excel"] = delegate()
                {
                    P_Grid.ExportToXls();
                };
                if (P_Grid.OptionsBehavior.Editable)
                {
                    L_Commands["Paste"] = delegate()
                    {
                        P_Grid.PasteValues();
                    };
                    if (P_Grid.OptionsBehavior.Editable == true)
                    {
                        L_Commands["Clear"] = delegate()
                        {
                            if (MessageBoxes.Confirm("Delete all lines ?"))
                            {
                                P_DataSource.Clear();
                            }
                        };
                    }
                }
                ViewBase L_Form = P_Grid.FindForm() as ViewBase;
                if (L_Form != null)
                {
                    P_Grid.MenuManager = L_Form.BMA_Main;
                    P_Grid.ToolTipController = L_Form.TOO_Controller;
                }
                AddPopupMenu(P_Grid, L_Commands);
            }
        }

        #region View Export
        /// <summary>
        /// Exports the View's data to a file in XLS format.
        /// </summary>
        /// <param name="P_View">View Control</param>
        public static void ExportToXls(this GridView P_View)
        {
            try
            {
                string tempfile = String.Format("{0}export_excel_{1}.xls", Path.GetTempPath(), System.DateTime.Now.Ticks);
                P_View.OptionsPrint.AutoWidth = false;
                _Log.InfoFormat("Exporting to {0}", tempfile);
                using (Stream str = new MemoryStream())
                {
                    P_View.SaveLayoutToStream(str);
                    str.Seek(0, System.IO.SeekOrigin.Begin);
                    P_View.BestFitColumns();
                    P_View.OptionsView.ColumnAutoWidth = false;
                    XlsExportOptions L_Options = new XlsExportOptions(TextExportMode.Value);
                    L_Options.ExportMode = XlsExportMode.SingleFile;
                    P_View.ExportToXls(tempfile, L_Options);
                    System.Diagnostics.Process.Start(tempfile);
                    P_View.RestoreLayoutFromStream(str);
                }
            }
            catch (Exception Ex)
            {
                _Log.Error("Internal error", Ex);
                MessageBoxes.Warning("Internal error while exporting");
            }
        }

        public static void ExportToXls(this VGridControl P_View)
        {
            try
            {
                string tempfile = String.Format("{0}export_excel_{1}.xls", Path.GetTempPath(), System.DateTime.Now.Ticks);
                _Log.InfoFormat("Exporting to {0}", tempfile);
                using (Stream str = new MemoryStream())
                {
                    P_View.SaveLayoutToStream(str);
                    str.Seek(0, System.IO.SeekOrigin.Begin);
                    P_View.BestFit();
                    XlsExportOptions L_Options = new XlsExportOptions(TextExportMode.Value);
                    L_Options.ExportMode = XlsExportMode.SingleFile;
                    P_View.ExportToXls(tempfile, L_Options);
                    System.Diagnostics.Process.Start(tempfile);
                    P_View.RestoreLayoutFromStream(str);
                }
            }
            catch (Exception Ex)
            {
                _Log.Error("Internal error", Ex);
                MessageBoxes.Warning("Internal error while exporting");
            }
        }

        public static void ExportToPdf(this GridView P_View)
        {
            try
            {
                string tempfile = String.Format("{0}export_pdf_{1}.pdf", Path.GetTempPath(), System.DateTime.Now.Ticks);
                P_View.OptionsPrint.AutoWidth = false;
                _Log.InfoFormat("Exporting to {0}", tempfile);
                using (Stream str = new MemoryStream())
                {
                    P_View.SaveLayoutToStream(str);
                    str.Seek(0, System.IO.SeekOrigin.Begin);
                    P_View.BestFitColumns();
                    P_View.OptionsView.ColumnAutoWidth = false;
                    P_View.ExportToPdf(tempfile);
                    System.Diagnostics.Process.Start(tempfile);
                    P_View.RestoreLayoutFromStream(str);
                }
            }
            catch (Exception Ex)
            {
                _Log.Error("Internal error", Ex);
                MessageBoxes.Warning("Internal error while exporting");
            }
        }
        #endregion

        #region Paste
        public static void PasteValues(this VGridControl P_View)
        {
            // check visible columns & grouping
            if (P_View.VisibleRows.Count == 0)
            {
                MessageBoxes.Message("No visible rows to paste data");
                return;
            }

            int L_StartIndex = 0;

            // get clipboard values
            if (!Clipboard.ContainsText())
                return;

            string L_ClipBoardBulkData = Clipboard.GetText();

            try
            {
                List<ValidationMessage> L_Messages = new List<ValidationMessage>();

                // cancel current edition
                if (P_View.FocusedRecord > 0)
                {
                    P_View.CancelUpdateFocusedRecord();
                }

                // add new row
                P_View.AddNewRecord();

                P_View.BeginDataUpdate();

                string[] L_Lines = L_ClipBoardBulkData.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                List<string[]> L_Matrix = new List<string[]>();
                foreach (var L_Line in L_Lines)
                {
                    L_Matrix.Add(L_Line.Split('\t'));
                }

                List<List<string>> L_ReversedMatrix = new List<List<string>>();
                if (L_Matrix.Count > 0)
                {
                    // get first line as length;

                    for (int L_RecordIndex = 0; L_RecordIndex < L_Matrix[0].Length; L_RecordIndex++)
                    {
                        List<string> L_Record = new List<string>();
                        for (int L_Line = 0; L_Line < L_Matrix.Count; L_Line++)
                        {
                            L_Record.Add(L_Matrix[L_Line][L_RecordIndex]);
                        }
                        L_ReversedMatrix.Add(L_Record);
                    }
                }


                for (int j = 0; j < L_ReversedMatrix.Count; j++)
                {
                    string[] L_CellValues = L_ReversedMatrix[j].ToArray();
                    // detect header
                    if (L_CellValues.Length > 0 && L_StartIndex < P_View.VisibleRows.Count)
                    {
                        if (!string.IsNullOrEmpty(L_CellValues[0]) && L_CellValues[0] == P_View.VisibleRows[L_StartIndex].Properties.Caption)
                            continue;
                    }

                    for (int i = 0; i < L_CellValues.Length && i + L_StartIndex < P_View.VisibleRows.Count; i++)
                    {
                        EditorRow L_Column = (EditorRow)P_View.VisibleRows[L_StartIndex + i];
                        if (!L_Column.Properties.ReadOnly)
                        {
                            object L_ValueToSet = L_CellValues[i];

                            // lookup edit
                            if (L_Column.Properties.RowEdit is RepositoryItemLookUpEdit)
                            {
                                RepositoryItemLookUpEdit L_Editor = L_Column.Properties.RowEdit as RepositoryItemLookUpEdit;
                                L_ValueToSet = L_Editor.GetKeyValueByDisplayText(L_CellValues[i]);
                            }

                            // text edit
                            if (L_Column.Properties.RowEdit is RepositoryItemTextEdit)
                            {
                                RepositoryItemTextEdit L_Editor = L_Column.Properties.RowEdit as RepositoryItemTextEdit;
                                if (L_CellValues[i].Contains('%') && (
                                    L_Column.Properties.RowType == typeof(double)
                                    || L_Column.Properties.RowType == typeof(double?)
                                    || L_Column.Properties.RowType == typeof(decimal)
                                    || L_Column.Properties.RowType == typeof(decimal?)))
                                {
                                    if (L_Editor.Mask.MaskType == DevExpress.XtraEditors.Mask.MaskType.None)
                                    {
                                        L_ValueToSet = double.Parse(L_CellValues[i].Replace("%", "")) / 100;
                                    }
                                    else if (L_Editor.Mask.EditMask.Contains("P"))
                                    {
                                        L_ValueToSet = double.Parse(L_CellValues[i].Replace("%", ""));
                                    }
                                }
                            }

                            if (L_Column.Properties.RowType == typeof(bool))
                            {
                                string L_Text = L_CellValues[i].ToLower();
                                if (string.IsNullOrEmpty(L_Text) || L_Text == "unchecked" || L_Text == "false" || L_Text == "no")
                                {
                                    L_ValueToSet = false;
                                }
                                if (L_Text == "checked" || L_Text == "true" || L_Text == "yes")
                                {
                                    L_ValueToSet = true;
                                }
                            }

                            // gridlookup edit
                            if (L_Column.Properties.RowEdit is RepositoryItemGridLookUpEdit)
                            {
                                RepositoryItemGridLookUpEdit L_Editor = L_Column.Properties.RowEdit as RepositoryItemGridLookUpEdit;
                                IList L_ListSource = L_Editor.DataSource as IList;
                                if (L_ListSource != null)
                                {
                                    for (int k = 0; k < L_ListSource.Count; k++)
                                    {
                                        if (L_Editor.GetDisplayText(k) == L_CellValues[i])
                                        {
                                            L_ValueToSet = L_Editor.GetKeyValue(k);
                                            break;
                                        }
                                    }
                                }
                            }
                            InvalidValueExceptionEventHandler L_InvalidValueHandler = delegate(object sender, InvalidValueExceptionEventArgs e)
                            {
                                e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.NoAction;
                                L_Messages.Add(new ValidationMessage() { Type = ErrorType.Warning, Message = string.Format("Error on line {0} : invalid L_Valueueue '{1}' for column {2}", j, L_CellValues[i], L_Column.Properties.Caption) });
                            };
                            P_View.InvalidValueException += L_InvalidValueHandler;
                            P_View.SetCellValue(L_Column.Properties, j, L_ValueToSet);
                            P_View.InvalidValueException -= L_InvalidValueHandler;
                        }
                    }
                    InvalidRecordExceptionEventHandler L_Handler = delegate(object sender, InvalidRecordExceptionEventArgs e)
                    {
                        e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.NoAction;
                        L_Messages.Add(new ValidationMessage() { Type = ErrorType.Default, Message = string.Format("Error on line {0} : {1}", j, e.ErrorText) });
                    };

                    P_View.InvalidRecordException += L_Handler;
                    bool L_Result = P_View.UpdateFocusedRecord();

                    P_View.InvalidRecordException -= L_Handler;

                    if (j != L_ReversedMatrix.Count - 1 && L_Result)
                    {
                        P_View.AddNewRecord();
                    }
                }

                // cancel current edition if active
                if (P_View.FocusedRecord > 0)
                {
                    P_View.CancelUpdateFocusedRecord();
                }

                if (L_Messages.Count > 0)
                    InputErrorMessageBox.DisplayErrorSummary(L_Messages.ToArray());
            }
            catch (Exception Ex)
            {
                MessageBoxes.Warning("Error while pasting data : ", Ex.Message);
                _Log.Error("Error while pasting data", Ex);
            }
            finally
            {
                P_View.EndDataUpdate();
            }

        }

        public static void PasteValues(this GridView P_View)
        {
            // check visible columns & grouping
            if (P_View.GroupCount > 0)
            {
                MessageBoxes.Message("Unable to paste _Values when grouping");
                return;
            }
            if (P_View.VisibleColumns.Count == 0)
            {
                MessageBoxes.Message("No visible columns to paste data");
                return;
            }

            int L_StartIndex = 0;

            // get clipboard values
            if (!Clipboard.ContainsText())
                return;

            string L_ClipBoardBulkData = Clipboard.GetText();

            try
            {
                List<ValidationMessage> L_Messages = new List<ValidationMessage>();
                Dictionary<RepositoryItem, ConcurrentDictionary<string, object>> L_EditorCache = new Dictionary<RepositoryItem, ConcurrentDictionary<string, object>>();

                // cancel current edition
                if (P_View.FocusedRowHandle == GridControl.NewItemRowHandle || P_View.IsEditing)
                {
                    P_View.CancelUpdateCurrentRow();
                }

                // add new row
                P_View.AddNewRow();

                P_View.BeginDataUpdate();

                string[] L_Lines = L_ClipBoardBulkData.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                for (int j = 0; j < L_Lines.Length; j++)
                {
                    string L_Line = L_Lines[j];

                    string[] L_CellValues = L_Line.Split('\t');
                    // detect header
                    if (L_CellValues.Length > 0 && L_StartIndex < P_View.VisibleColumns.Count)
                    {
                        if (!string.IsNullOrEmpty(L_CellValues[0]) && L_CellValues[0] == P_View.VisibleColumns[L_StartIndex].Caption)
                            continue;
                    }

                    for (int i = 0; i < L_CellValues.Length && i + L_StartIndex < P_View.VisibleColumns.Count; i++)
                    {
                        GridColumn L_Column = P_View.VisibleColumns[L_StartIndex + i];
                        if (L_Column.OptionsColumn.AllowEdit)
                        {
                            object L_ValueToSet = L_CellValues[i];

                            // lookup edit
                            if (L_Column.ColumnEdit is RepositoryItemLookUpEdit)
                            {
                                RepositoryItemLookUpEdit L_Editor = L_Column.ColumnEdit as RepositoryItemLookUpEdit;
                                L_ValueToSet = L_Editor.GetKeyValueByDisplayText(L_CellValues[i]);
                            }

                            // text edit
                            if (L_Column.ColumnEdit is RepositoryItemTextEdit)
                            {
                                RepositoryItemTextEdit L_Editor = L_Column.ColumnEdit as RepositoryItemTextEdit;

                                if (L_CellValues[i].Contains('%') && (
                                    L_Column.ColumnType == typeof(double)
                                    || L_Column.ColumnType == typeof(double?)
                                    || L_Column.ColumnType == typeof(decimal)
                                    || L_Column.ColumnType == typeof(decimal?)))
                                {
                                    if (L_Editor.Mask.MaskType == DevExpress.XtraEditors.Mask.MaskType.None)
                                    {
                                        L_ValueToSet = double.Parse(L_CellValues[i].Replace("%", "")) / 100;
                                    }
                                    else if (L_Editor.Mask.EditMask.Contains("P"))
                                    {
                                        L_ValueToSet = double.Parse(L_CellValues[i].Replace("%", ""));
                                    }
                                }
                            }

                            if (L_Column.ColumnType == typeof(bool))
                            {
                                string L_Text = L_CellValues[i].ToLower();
                                if (string.IsNullOrEmpty(L_Text) || L_Text == "unchecked" || L_Text == "false" || L_Text == "no")
                                {
                                    L_ValueToSet = false;
                                }
                                if (L_Text == "checked" || L_Text == "true" || L_Text == "yes")
                                {
                                    L_ValueToSet = true;
                                }
                            }

                            // gridlookup edit
                            if (L_Column.ColumnEdit is RepositoryItemGridLookUpEdit)
                            {
                                RepositoryItemGridLookUpEdit L_Editor = L_Column.ColumnEdit as RepositoryItemGridLookUpEdit;
                                IList L_ListSource = L_Editor.DataSource as IList;

                                // set cache node
                                if (!L_EditorCache.ContainsKey(L_Editor))
                                {
                                    L_EditorCache.Add(L_Editor, new ConcurrentDictionary<string, object>());
                                }
                                var L_Cache = L_EditorCache[L_Editor];

                                if (L_Cache.ContainsKey(L_CellValues[i]))
                                {
                                    L_ValueToSet = L_Cache[L_CellValues[i]];
                                }
                                else if (L_ListSource != null)
                                {
                                    //Parallel.For(0, L_ListSource.Count, (k, s) =>
                                    for (int k = 0; k < L_ListSource.Count; k++)
                                    {
                                        if (L_Editor.GetDisplayText(k) == L_CellValues[i])
                                        {
                                            L_ValueToSet = L_Editor.GetKeyValue(k);
                                            L_Cache[L_CellValues[i]] = L_ValueToSet;
                                            break;
                                        }
                                    }
                                }
                            }
                            InvalidValueExceptionEventHandler L_InvalidValueHandler = delegate(object sender, InvalidValueExceptionEventArgs e)
                            {
                                e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.NoAction;
                                L_Messages.Add(new ValidationMessage() { Type = ErrorType.Warning, Message = string.Format("Error on line {0} : invalid L_Valueueue '{1}' for column {2}", j, L_CellValues[i], L_Column.Caption) });
                            };
                            P_View.InvalidValueException += L_InvalidValueHandler;
                            P_View.SetFocusedRowCellValue(L_Column, L_ValueToSet);
                            P_View.InvalidValueException -= L_InvalidValueHandler;
                        }
                    }
                    InvalidRowExceptionEventHandler L_Handler = delegate(object sender, InvalidRowExceptionEventArgs e)
                    {
                        e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.NoAction;
                        L_Messages.Add(new ValidationMessage() { Type = ErrorType.Default, Message = string.Format("Error on line {0} : {1}", j, e.ErrorText) });
                    };

                    P_View.InvalidRowException += L_Handler;
                    bool L_Result = P_View.UpdateCurrentRow();

                    P_View.InvalidRowException -= L_Handler;

                    if (j != L_Lines.Length - 1 && L_Result)
                    {
                        P_View.AddNewRow();
                    }
                }

                // cancel current edition if active
                if (P_View.FocusedRowHandle == GridControl.NewItemRowHandle || P_View.IsEditing)
                {
                    P_View.CancelUpdateCurrentRow();
                }

                if (L_Messages.Count > 0)
                    InputErrorMessageBox.DisplayErrorSummary(L_Messages.ToArray());
            }
            catch (Exception Ex)
            {
                MessageBoxes.Warning("Error while pasting data : ", Ex.Message);
                _Log.Error("Error while pasting data", Ex);
            }
            finally
            {
                P_View.EndDataUpdate();
            }

        }
        #endregion

        #region Right click menu
        private static object _BarManagerLock = new object();
        private static Dictionary<Control, BarManager> _Managers = new Dictionary<Control, BarManager>();
        private static BarManager GetBarManager(Control P_Control)
        {
            ViewBase L_View = P_Control.FindForm() as ViewBase;
            if (L_View == null)
                return null;
            else
                return L_View.BMA_Main;
        }

        public static void AddPopupMenu(VGridControl P_Grid, Dictionary<string, Action> P_Commands)
        {
            BarManager L_Manager = GetBarManager(P_Grid);
            PopupMenu L_GridMenu = new PopupMenu(L_Manager);
            // export (construction des éléments à améliorer)
            if (P_Commands.Where(x => x.Key.StartsWith("Export")).Any())
            {
                BarSubItem L_Export = new BarSubItem();
                L_Export.Caption = "Export";
                L_GridMenu.ItemLinks.Add(L_Export);

                // excel
                if (P_Commands.ContainsKey("Export.Excel"))
                {
                    BarButtonItem L_Excel = new BarButtonItem(L_Manager, "Excel");
                    L_Excel.ItemClick += (s, e) => P_Commands["Export.Excel"]();
                    L_Export.AddItem(L_Excel);
                }
            }

            if (P_Commands.ContainsKey("Paste"))
            {
                BarButtonItem L_Paste = new BarButtonItem(L_Manager, "Paste _Values");
                L_Paste.ItemClick += (s, e) => P_Commands["Paste"]();
                L_GridMenu.AddItem(L_Paste);
            }

            if (P_Commands.ContainsKey("Clear"))
            {
                BarButtonItem L_Clear = new BarButtonItem(L_Manager, "Clear");
                L_Clear.ItemClick += (s, e) => P_Commands["Clear"]();
                L_GridMenu.AddItem(L_Clear);
            }

            if (L_GridMenu.ItemLinks.Count > 0)
            {
                P_Grid.OptionsMenu.EnableContextMenu = true;
                L_Manager.SetPopupContextMenu(P_Grid, L_GridMenu);
                P_Grid.PopupMenuShowing += delegate(object sender, DevExpress.XtraVerticalGrid.Events.PopupMenuShowingEventArgs e)
                {
                    if (e.Row != null)
                    {
                        L_GridMenu.ShowPopup(Control.MousePosition);
                    }
                };
            }
        }

        public static void AddPopupMenu(GridView P_Grid, Dictionary<string, Action> P_Commands)
        {
            // Building Menu
            GridViewMenu L_GridMenu = new GridViewMenu(P_Grid);

            // export (construction des éléments à améliorer)
            if (P_Commands.Where(x => x.Key.StartsWith("Export")).Any())
            {
                DXSubMenuItem L_Export = new DXSubMenuItem();
                L_Export.Caption = "Export";
                L_GridMenu.Items.Add(L_Export);

                // excel
                if (P_Commands.ContainsKey("Export.Excel"))
                {
                    DXMenuItem L_Excel = new DXMenuItem("Excel", (s, e) => P_Commands["Export.Excel"]());
                    L_Export.Items.Add(L_Excel);
                }

                // excel
                if (P_Commands.ContainsKey("Export.PDF"))
                {
                    DXMenuItem L_PDF = new DXMenuItem("PDF", (s, e) => P_Commands["Export.PDF"]());

                    L_Export.Items.Add(L_PDF);
                }
            }

            if (P_Commands.ContainsKey("Paste"))
            {
                DXMenuItem L_Paste = new DXMenuItem("Paste _Values", (s, e) => P_Commands["Paste"]());
                L_GridMenu.Items.Add(L_Paste);
            }

            if (P_Commands.ContainsKey("Clear"))
            {
                DXMenuItem L_Clear = new DXMenuItem("Clear", (s, e) => P_Commands["Clear"]());
                L_GridMenu.Items.Add(L_Clear);
            }

            if (L_GridMenu.Items.Count > 0)
            {
                P_Grid.PopupMenuShowing += delegate(object sender, DevExpress.XtraGrid.Views.Grid.PopupMenuShowingEventArgs e)
                {
                    if (e.HitInfo.InRow || e.HitInfo.HitTest == GridHitTest.EmptyRow)
                    {
                        L_GridMenu.Show(e.HitInfo.HitPoint);
                    }
                };
            }
        }
        #endregion

        #region Columns
        public static void Configure<T>(this GridColumn P_Column, Expression<Func<T, object>> P_PopupDisplayMember, Expression<Func<T, object>> P_DisplayMember, Expression<Func<T, object>> P_ValueMember, LokiCollection<T> P_Datasource, string P_EmptyText = null)
        {
            RepositoryItemLookUpEdit L_Edit = CreateLookupEditor<T>(string.Format("{0}|{1}", P_Column.Name, typeof(T).Name), P_PopupDisplayMember, P_DisplayMember, P_ValueMember, P_EmptyText);
            P_Column.ColumnEdit = L_Edit;
            L_Edit.DataSource = P_Datasource;
        }

        public static RepositoryItemLookUpEdit CreateLookupEditor<T>(string P_EditorName, Expression<Func<T, object>> P_PopupDisplayMember, Expression<Func<T, object>> P_DisplayMember, Expression<Func<T, object>> P_ValueMember, string P_EmptyText)
        {
            RepositoryItemLookUpEdit L_Edit = new RepositoryItemLookUpEdit();
            try
            {
                ((System.ComponentModel.ISupportInitialize)(L_Edit)).BeginInit();
                L_Edit.AutoHeight = false;
                L_Edit.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] {
            new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)});
                L_Edit.Columns.AddRange(new DevExpress.XtraEditors.Controls.LookUpColumnInfo[] {
            new DevExpress.XtraEditors.Controls.LookUpColumnInfo(ExpressionHelper.Reflect<T>.GetProperty(P_PopupDisplayMember).Name, "Name", 20, DevExpress.Utils.FormatType.None, "", true, DevExpress.Utils.HorzAlignment.Default, DevExpress.Data.ColumnSortOrder.Ascending)});
                L_Edit.DisplayMember = ExpressionHelper.Reflect<T>.GetProperty(P_DisplayMember).Name;
                L_Edit.Name = P_EditorName;
                L_Edit.ShowFooter = false;
                L_Edit.ShowHeader = false;
                L_Edit.NullText = P_EmptyText;
                L_Edit.TextEditStyle = DevExpress.XtraEditors.Controls.TextEditStyles.Standard;
                L_Edit.ValueMember = ExpressionHelper.Reflect<T>.GetProperty(P_ValueMember).Name;

                ((System.ComponentModel.ISupportInitialize)(L_Edit)).EndInit();

                return L_Edit;
            }
            catch
            {
                L_Edit.Dispose();
                throw;
            }
        }

        public static void ConfigureEnum<T>(this GridColumn P_Column, string P_EmptyText, bool P_UseValueAsDisplay = false)
        {
            var L_ValueList = from x in Enum.GetValues(typeof(T)).OfType<T>()
                              select Tuple.Create<string, string>(x.ToString(), EnumParser<T>.ToText(x));
            RepositoryItemLookUpEdit L_Edit = null;
            if (P_UseValueAsDisplay)
                L_Edit = CreateLookupEditor<Tuple<string, string>>(string.Format("{0}|{1}", P_Column.Name, typeof(T).Name), x => x.Item1, x => x.Item2, x => x.Item1, P_EmptyText);
            else
                L_Edit = CreateLookupEditor<Tuple<string, string>>(string.Format("{0}|{1}", P_Column.Name, typeof(T).Name), x => x.Item2, x => x.Item2, x => x.Item1, P_EmptyText);
            P_Column.ColumnEdit = L_Edit;
            L_Edit.DataSource = new LokiCollection<Tuple<string, string>>(L_ValueList.ToList());
        }

        #endregion
    }
}
